/*
 * alfcrypto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alfcrypto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package com.fegor.alfresco.action;

import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.HashMap;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

import com.fegor.alfresco.model.AlfCryptoModel;
import com.fegor.alfresco.security.crypto.Crypto;
import com.google.gdata.util.common.util.Base64;

/**
 * CryptoRepo Action
 * 
 * @author fegor
 * 
 */
public class CipherContent extends ActionExecuterAbstractBase {

	private final Logger logger = Logger.getLogger(CipherContent.class);

	/*
	 * Services
	 */
	private ContentService contentService;
	private NodeService nodeService;

	private String password;
	//
	// TODO Poder usar más algoritmos que AES
	//
	// private String algorithm;

	private String salt;
	private String vector_init;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl
	 * (org.alfresco.service.cmr.action.Action,
	 * org.alfresco.service.cmr.repository.NodeRef)
	 */
	@Override
	protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
		if (!nodeService.hasAspect(actionedUponNodeRef,
				AlfCryptoModel.ASPECT_CIPHERED)) {
			if (logger.isDebugEnabled()) {
				logger.debug(this.getClass().getName() + ": [Action for: "
						+ actionedUponNodeRef + " is ciphering...]");
			}
			if (actionedUponNodeRef != null)
				try {
					this.cryptoFileCipher(actionedUponNodeRef);
				} catch (ContentIOException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#
	 * addParameterDefinitions(java.util.List)
	 */
	@Override
	protected void addParameterDefinitions(List<ParameterDefinition> arg0) {
	}

	/**
	 * Crypto file for nodeRef
	 * 
	 * @param nodeRef
	 * @throws IOException
	 * @throws ContentIOException
	 */
	private void cryptoFileCipher(NodeRef nodeRef) throws ContentIOException,
			IOException {
		ContentReader contentReader = this.contentService.getReader(nodeRef,
				ContentModel.PROP_CONTENT);
		ContentWriter contentWriter = this.contentService.getWriter(nodeRef,
				ContentModel.PROP_CONTENT, true);

		if (contentReader != null) {
			Crypto crypto = new Crypto();
			crypto.setPassword(this.password);
			byte[] crb = IOUtils.toByteArray(contentReader
					.getContentInputStream());

			try {
				crypto.configEncrypt();
			} catch (InvalidKeyException e) {
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			} catch (InvalidKeySpecException e) {
				e.printStackTrace();
			} catch (NoSuchPaddingException e) {
				e.printStackTrace();
			} catch (InvalidParameterSpecException e) {
				e.printStackTrace();
			} catch (IllegalBlockSizeException e) {
				e.printStackTrace();
			} catch (BadPaddingException e) {
				e.printStackTrace();
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}

			crypto.setInput(crb);
			try {
				crypto.Cipher();
			} catch (IllegalBlockSizeException e) {
				e.printStackTrace();
			} catch (BadPaddingException e) {
				e.printStackTrace();
			} catch (ShortBufferException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

			contentWriter.putContent(Base64.encode(crypto.getOutput()));

			this.salt = crypto.getSalt();
			this.vector_init = crypto.getVectorInit();

			this.removeAspect(nodeRef);
			this.addAspect(nodeRef);

		} else {
			if (logger.isDebugEnabled())
				logger.debug(this.getClass().getName()
						+ ": [contentReader is null]");
		}
	}

	/**
	 * Remove aspect Deciphered
	 * 
	 * @param nodeRef
	 */
	private void removeAspect(NodeRef nodeRef) {
		if (nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_DECIPHERED)) {
			nodeService.removeAspect(nodeRef, AlfCryptoModel.ASPECT_DECIPHERED);
		}
	}

	/**
	 * Add aspect Ciphered
	 * 
	 * @param nodeRef
	 */
	private void addAspect(NodeRef nodeRef) {
		HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>(
				1, 1.0f);
		properties.put(AlfCryptoModel.PROP_SALT, this.salt);
		properties.put(AlfCryptoModel.PROP_VECTOR_INIT, this.vector_init);
		if (!nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED)) {
			nodeService.addAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED,
					properties);
		}
	}

	/**
	 * @param contentService
	 */
	public void setContentService(ContentService contentService) {
		this.contentService = contentService;
	}

	/**
	 * @param nodeService
	 */
	public void setNodeService(NodeService nodeService) {
		this.nodeService = nodeService;
	}

	/**
	 * @param password
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @param algorithm
	 */
	//
	// TODO Poder usar más algoritmos que AES
	//
	// public void setAlgorithm(String algorithm) {
	// this.algorithm = algorithm;
	// }
}
